package web

import (
	"bytes"
	"errors"
	"fmt"
	"gitee.com/xiao_hange/go-admin-pkg/pkg/tmpl"
	"github.com/ecodeclub/ekit/slice"
	"github.com/gin-gonic/gin"
	"go-admin/server/internal/domain"
	"go-admin/server/internal/service"
	"go-admin/server/internal/web/form"
	"go-admin/server/internal/web/vo"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
	"strings"
)

var _ handler = &PackHandler{}

type PackHandler struct {
	svc service.PackServer
}

func NewPackHandler(svc service.PackServer) *PackHandler {
	return &PackHandler{
		svc: svc,
	}
}

func (p *PackHandler) RegisterRoutes(server *gin.Engine) {
	pg := server.Group("/pack")

	pg.POST("/create", p.create)
	pg.GET("/lst", p.lst)
}

func (p *PackHandler) create(ctx *gin.Context) {
	var req form.PackReq
	if err := ctx.Bind(&req); err != nil {
		RenderJSON(ctx, http.StatusOK, http.StatusNoContent, "参数解析错误", nil)
		return
	}
	err := p.generateWebLayer(req.PackName, true)
	if err != nil {
		RenderJSON(ctx, http.StatusOK, http.StatusNoContent, "添加失败"+err.Error(), nil)
		return
	}
	err = p.generateServiceLayer(req.PackName, true)
	if err != nil {
		RenderJSON(ctx, http.StatusOK, http.StatusNoContent, "添加失败"+err.Error(), nil)
		return
	}
	err = p.generateRepositoryLayer(req.PackName, true)
	if err != nil {
		RenderJSON(ctx, http.StatusOK, http.StatusNoContent, "添加失败"+err.Error(), nil)
		return
	}
	err = p.generateDAOLayer(req.PackName)
	if err != nil {
		RenderJSON(ctx, http.StatusOK, http.StatusNoContent, "添加失败"+err.Error(), nil)
		return
	}
	_ = p.svc.Create(ctx.Request.Context(), domain.Pack{
		PackName: req.PackName,
		Describe: req.Describe,
	})
	RenderJSON(ctx, http.StatusOK, http.StatusOK, "添加成功", nil)
	return
}

func (p *PackHandler) lst(ctx *gin.Context) {
	var req form.PageReq
	err := ctx.Bind(&req)
	if err != nil {
		return
	}
	lists, total, err := p.svc.GetPackLst(ctx.Request.Context(), int(req.Page), int(req.Limit))

	data := slice.Map[domain.Pack, vo.PackVO](lists,
		func(idx int, src domain.Pack) vo.PackVO {
			return vo.PackVO{
				Id:         src.ID,
				PackName:   src.PackName,
				Describe:   src.Describe,
				CreateTime: src.CreateTime,
			}
		})
	RenderJSONPage(ctx, http.StatusOK, http.StatusOK, "ok！", data, int64(total))
	return
}

// 生成Web层代码
func (p *PackHandler) generateWebLayer(name string, isService bool) error {
	name = strings.Title(name)
	fileName := fmt.Sprintf("%s.go", strings.ToLower(name))
	receiverName := fmt.Sprintf("%s *%sHandler", strings.ToLower(string(name[0])), name)
	// 检查是否存在internal/web目录，如果不存在则创建
	directory := "internal/web"
	return p.assignTemplateToFiles(directory, fileName, name, receiverName, tmpl.WebTmpl, isService)

}

// 生成Service层代码
func (p *PackHandler) generateServiceLayer(name string, isRepo bool) error {
	name = strings.Title(name)
	fileName := fmt.Sprintf("%s.go", strings.ToLower(name))
	directory := "internal/service"
	return p.assignTemplateToFiles(directory, fileName, name, strings.ToLower(name), tmpl.ServerTmpl, isRepo)
}

// 生成Repository层代码
func (p *PackHandler) generateRepositoryLayer(name string, isDao bool) error {
	name = strings.Title(name)
	fileName := fmt.Sprintf("%s.go", strings.ToLower(name))
	directory := "internal/repository"
	return p.assignTemplateToFiles(directory, fileName, name, strings.ToLower(name), tmpl.RepositoryTmpl, isDao)
}

// 生成DAO层代码
func (p *PackHandler) generateDAOLayer(name string) error {
	name = strings.Title(name)
	fileName := fmt.Sprintf("%s.go", strings.ToLower(name))
	directory := "internal/repository/dao"
	return p.assignTemplateToFiles(directory, fileName, name, strings.ToLower(name), tmpl.DaoTmpl, false)
}

func (p *PackHandler) assignTemplateToFiles(directory, fileName, name, receiverName, webTmpl string, isGenerate bool) error {
	if _, err := os.Stat(directory); os.IsNotExist(err) {
		err = os.MkdirAll(directory, os.ModePerm)
		if err != nil {
			return err
		}
	}
	filePath := filepath.Join(directory, fileName)
	if p.fileExists(filePath) {
		return errors.New("文件已存在")
	}
	data := struct {
		TypeName     string
		ReceiverName string
		GroupName    string
		IsService    bool
		IsRepo       bool
		IsDao        bool
	}{
		TypeName:     name,
		ReceiverName: receiverName,
		GroupName:    strings.ToLower(name),
		IsService:    isGenerate,
		IsRepo:       isGenerate,
		IsDao:        isGenerate,
	}

	t, err := template.New(name).Parse(webTmpl)
	if err != nil {
		return err
	}

	// 创建一个缓冲区来收集模板生成的内容
	var buf bytes.Buffer

	// 执行模板替换，并将结果写入缓冲区
	err = t.Execute(&buf, data)
	if err != nil {
		return err
	}

	generatedCode := buf.String()

	fset := token.NewFileSet()
	node, err := parser.ParseFile(fset, fileName, generatedCode, parser.ParseComments)
	if err != nil {
		return err
	}

	// 修改 AST
	p.updateAST(node)
	var buf1 bytes.Buffer
	// 格式化修改后的源代码
	err = format.Node(os.Stdout, fset, node)
	if err != nil {
		return err
	}
	// 生成 Go 代码字符串
	err = format.Node(&buf1, fset, node)
	if err != nil {
		return err
	}
	// 创建并写入修改后的源代码文件
	if err = p.writeToFile(filePath, []byte(buf1.String())); err != nil {
		return err
	}
	return nil
}

func (p *PackHandler) updateAST(node *ast.File) {
	ast.Inspect(node, func(n ast.Node) bool {
		switch v := n.(type) {
		case *ast.StructType:
			existingFields := v.Fields.List
			v.Fields.List = nil
			v.Fields.List = append(v.Fields.List, existingFields...)
		}
		return true
	})
}

// 写入文件
func (p *PackHandler) writeToFile(fileName string, code []byte) error {
	file, err := os.Create(fileName)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.Write(code)
	return err
}

func (p *PackHandler) fileExists(filename string) bool {
	_, err := os.Stat(filename)
	return !os.IsNotExist(err)
}
